<!DOCTYPE html>
<html lang="ch">
<head>
    <meta charset="UTF-8">
    <title>字符串</title>
</head>
<body>

<script type="text/javascript">
/** Unicode **/
// 采用\uxxxx形式表示一个字符 "\u0061"=="a" "\u20BB7"==' 7'
// 只限于码点在\u0000~\uFFFF之间的字符 超出这个范围的字符，必须用两个双字节的形式表示。
// 大括号表示法与四字节的 UTF-16 编码是等价的 "\u{20BB7}"=='吉'

/** for...of **/
// 字符串的遍历器接口 可识别大于0xFFFF的码点
// String.fromCodePoint() 返回使用指定的代码点序列创建的字符串。
for (let codePoint of 'foo') {
    // console.log(codePoint)
}
let text = String.fromCodePoint(0x20BB7);
for (let i of text) {
    // console.log(i); // 吉
}
/** 直接输入 U+2028 和 U+2029 **/
// JavaScript规定有5个字符不能在字符串里面直接使用,只能使用转义形式。
//   \U005C:反斜杠 U\000D:回车 \U2028:行分隔符 \U2029:段分隔符 \U000A:换行符
    let hh = '\u000A' // 输出的是一个换行符
// 但在JOSN格式里允许直接使用行分隔符和段分隔符
JSON.parse('{"a":"\u2028"}'); // 之前报错
// 所以es2019允许字符串直接输入 U+2028（行分隔符）和 U+2029（段分隔符）
JSON.parse('{"a":"\u2028"}'); // 不报错
//     JSON.parse('{"a":"\u000A"}'); // 报错
JSON.parse('{"a":"\\u000A"}'); // 不报错
// 模板字符串允许直接输入这两个字符,正则表达式不允许直接输入这两个字符,因为JSON本来就不允许直接包含正则表达式

/** JSON.stringify() **/
// 根据标准,JSON数据必须是UTF-8编码.但现在的JSON.stringify()方法有可能返回不符合UTF-8标准的字符串。
// JSON.stringify()的问题在于，它可能返回0xD800到0xDFFF之间的单个码点 JSON.stringify('\u{D834}') // "\u{D834}"
// UTF-8标准规定,0xD800到0xDFFF之间的码点,不能单独使用,必须配对使用.
//   比如，\uD834\uDF06是两个码点，但是必须放在一起配对使用，这是为了表示码点大于0xFFFF的字符的一种变通方法。
//   单独使用\uD834和\uDFO6这两个码点是不合法的，或者颠倒顺序也不行，因为\uDF06\uD834并没有对应的字符。
// ES2019改变了JSON.stringify()的行为。如果遇到0xD800到0xDFFF之间的单个码点，
//   或者不存在的配对形式，它会返回转义字符串，留给应用自己决定下一步的处理。
JSON.stringify('\u{D834}'); // ""\\uD834""
JSON.stringify('\uDF06\uD834'); // ""\\udf06\\ud834""

/** 模板字符串`` **/
// 增强版字符串,用反引号（`）标识.可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量
// 空格和缩进会保留在输出中  如果大括号中的值不是字符串，将按照一般的规则转为字符串
// 大括号内部可以放入任意的JavaScript表达式，可以进行运算，以及引用对象属性,还能调用函数，或者在嵌套一个模板字符串
    let a = `第一行${window}${3+3}
            第二行,变量${text}`;
// 如果需要引用模板字符串本身，在需要时执行，可以写成函数。
let func = (name) => `Hello ${name}!`;
func('Jack'); // "Hello Jack!"

/** 标签模板 **/
// 紧跟在一个函数名后面，该函数将被调用来处理这个模板字符串
func`标签模板`; // 等同于 func('标签模板')
// 标签模板其实不是模板，而是函数调用的一种特殊形式.“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数
// 如果模板字符里面有变量,会将模板字符串先处理成多个参数,再调用函数
let q = 5, b = 10;
tag`Hello ${ q + b } world ${ q * b }`;// 以以下形式调用
tag(['Hello ', ' world ', ''], 15, 50);
function tag(stringArr, value1, value2){} // 等同于以下形式创建
// function tag(stringArr, ...values){}

/** 模板字符串限制 */
// 模板字符串默认会将字符串转义,导致无法嵌入其他语言
// ES2018放松了对标签模板里面的字符串转义的限制.如果遇到不合法的字符串转义,
//     就返回undefined,而不是报错,并且从raw属性上面可以得到原始字符串

/** 新增方法 **/
// String.fromCodePoint() 从Unicode码点返回对应字符，能识别码点大于0xFFFF的字符
// String.raw()        模板字符串的标签函数,获取一个模板字符串的原始字符串的  
String.fromCharCode(0x20BB7); // "ஷ" ES5的fromCharCode()不能识别大于0xFFFF的码点,0x20BB7发生溢出,最高位'2'被舍弃,返回u+0BB7(0x20BB7)对应的字符
String.fromCodePoint(0x20BB7); // "𠮷" 正确识别
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y' // true 多个参数会被合并成一个字符串返回

</script>
</body>
</html>